define(function(require, exports, module) {
	function xhrFactoryForIE(){
		// 每当遇到一个try语句，异常的框架就放到堆栈上面，直到所有的try语句都完成。
		// 如果下一级的try语句没有对某种异常进行处理，堆栈就会展开，直到遇到有处理这种异常的try语句。
		// 这是十分浪费性能的做法，但暂时又找不到恰当的解决方式，于是采用了这种方法。
		if (window.ActiveXObject) {
			try {
				return new ActiveXObject("Msxml2.XMLHTTP.5.0");
			} catch (e) {
				try {
					return new ActiveXObject("Msxml2.XMLHTTP.4.0");
				} catch (e) {
					try {
						return new ActiveXObject("Microsoft.XMLHTTP");
					} catch (e) {}
				}
			}
		}else throw '没找到 ActiveXObject 对象，这不是 IE 浏览器吧！';
	}

	var xhr = {
		xhr : null,
		httpMethod : 'GET',
		timeout	: 20000,
		isAsync	: true, // ff 必须输入 Boolean，ie则没那么严格。
		url : '',
		isDone : false,
		request : function(){
			// 复制配置参数
			checkURL_CallBack(this);
			
			// 安排超时检查。虽可工作正常，有一点不解：为什么得把 timeout 设置在前面， delegate(null, this)不是 ByRef 的吗？
			if(this.timeout){
				var self = this;
				this.timoutId = window.setTimeout(function(){
					self.isDone != true && self.onFail(self.url + '\n已发出通讯消息，但服务端执行超时，反馈失败！');
				}, this.timeout);				
			}

			var xhr;
			xhr = window.XMLHttpRequest ? new XMLHttpRequest() : xhrFactoryForIE();
			xhr.open(this.httpMethod, this.httpMethod == 'POST' || this.httpMethod == 'PUT' ? this.url : appendParams2url(this.url, this.params), this.isAysc);
			// 为协调 ie，将 onreadystatechange 置于 open() 后。
			xhr.onreadystatechange = onreadystatechange.delegate(null, this);
			
			// 设置 HTTP 头
	        (this.httpMethod == 'POST' || this.httpMethod == 'PUT') && xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	        this.referer && xhr.setRequestHeader("Referer", this.referer);
	        this.contentType && xhr.setRequestHeader("Content-Type", this.contentType); // you may set xhr.setRequestHeader("Accept", "text/json");

	        try{
				// console.log('正在发起 HTTP 请求，url： ' + this.url);
				xhr.send(this.httpMethod == 'POST' || this.httpMethod == 'PUT' ? appendParams2url(null, this.params) : null);
			}catch(e){
				this.killTimeout();
				this.onFail('不能发起请求，请检查 url！');
			}

			this.xhr = xhr;
		},
		onSuccuess : function(responseText){
			alert('请求结束！ Results：' + responseText);
		},
		onFail : function(responseText){
			// alert('请求失败！原因' + responseText);
		},
		killTimeout: function(){
			this.timoutId && window.clearTimeout(this.timoutId);
		}
	}
	
	var trimReg = /(^\s*)|(\s*$)/g;
	/**
	 * @param {_XMLHttpRequestProgressEvent} native obj
	 * @param {$$.request} requestInstance
	 */
	function onreadystatechange(_XMLHttpRequestProgressEvent, requestInstance) {
		var xhr = this; // 当前scope 就是 那个 XHR 对象。
		if(xhr.status === 404){
			requestInstance.killTimeout();
			requestInstance.onFail('资源 {0} 不存在 404'.format(requestInstance.url));
		}else if(xhr.readyState === 4 && xhr.status !== 200) {
			// debugger;
			requestInstance.killTimeout();

			if(xhr.status === 500)requestInstance.onFail('HTTP State Code：500，服务器程序有误');
			else requestInstance.onFail('HTTP State Code：{0}，未知异常！'.format(xhr.status));
		}else if(xhr.readyState === 4 && xhr.status === 200) {
			requestInstance.isDone = true;

			var responseData, responseText = xhr.responseText.replace(trimReg, "");
			// Is that JSON String?
			if(responseText.charAt(0) === '{' || responseText.charAt(0) === '['){
				try{ // 兼容两者写法，一种是 key 带双引号的严格写法，另外一种是旧写法，key 允许不带引号
					if(responseText.charAt(1) === '"')
						responseData = window.JSON.parse(responseText);
					else throw 'use eval';
				}catch(e){
					responseData = window.eval('(' + responseText + ')');
				}
			}else responseData = responseText;// not json, just plain old text maybe HTML

			(requestInstance.onSuccuess || requestInstance.CallBack || requestInstance.callBack)(
				responseData, requestInstance, xhr
			);
			xhr = null; // 结束 xhr
		}else{
			console.log('注意，HTTP 状态码异常：' + xhr.status);
		}
	}

	function checkURL_CallBack(cfg){
		var onSuccuess = cfg.callBack || cfg.CallBack ||cfg.onSuccuess;

		if(!cfg.url)
			throw '[request]未指定服务端地址';
		else if(cfg.isAsync === false && !onSuccuess)
			throw '[request]未指定回调函数';
		else if(cfg.isAsync === false && typeof onSuccuess != 'function')
			throw '[request]指定回调函数参数其类型不是函数！';
		
		if(cfg.onFail && typeof cfg.onFail != 'function')
			throw '[request]指定失败的回调函数参数其类型不是函数！';

		cfg.httpMethod = cfg.httpMethod.toUpperCase(); // 强制大写
	}

	/**
	 * 参数的类型转换
	 * @param {Object} json
	 * @return {String}
	 */
	function appendParams2url(url, paramsAsJson){
		if(!paramsAsJson)return url;

		var paramsAsArr = [], paramsAsStr;
		for(var i in paramsAsJson)paramsAsArr.push(i + '=' + paramsAsJson[i]);
			paramsAsStr = paramsAsArr.join('&');

		if(!url)
			return paramsAsStr; // 第一个参数为 null 的话，表示只是 paramsAsJson--> paramsAsStr
		else if(~url.indexOf('?'))
			return url + '&' + paramsAsStr;
		else
			return url + '?' + paramsAsStr;
	}

	exports.xhr = xhr;
	// short hand
	function doRequest(url, callback, p, HTTP_METHOD){
		var _xhr = Object.create(xhr);
		_xhr.url = url;
		_xhr.params = p || {};
		if(HTTP_METHOD)
		_xhr.httpMethod = HTTP_METHOD; // 不输入 HTTP_METHOD，则默认是 GET 方法
		_xhr.onSuccuess = callback;
		_xhr.request();
		
		return _xhr;
	}
	exports.get 	= doRequest.delegate(null, null, null, null);
	exports.delet 	= doRequest.delegate(null, null, null, 'DELETE');
	exports.put 	= doRequest.delegate(null, null, null, 'PUT');
	exports.post 	= doRequest.delegate(null, null, null, 'POST');
	exports.head	= doRequest.delegate(null, null, null, 'HEAD'); // 还有一个生僻的请求方式：head，作用就是只向服务器请求head内容，不关心返回html正文

	/**
	 * 利用动态加载脚本来获取跨域的数据。支持：
	 * 回调函数
	 * 超时检测
	 * 自动 GC
	 */
	var jsonp = {
		url : '',
		timeout	: 2000,
		isDone : false,
		callBackField : 'callBack',// 默认值
		onSuccuess : xhr.onSuccuess,
		onFail : xhr.onFail,
		request : function(){
			var globalMethod_Token	= 'globalMethod_' + getRandom(), globalMethod = '$$_jsonp.' + globalMethod_Token;
			
			// 设置一个全局的空函数，然后 AOP 一下	
			// 获取回调的引用，不管在哪个闭包中
			if(!window.$$_jsonp){
				window.$$_jsonp = {};
			}
			// Map<String, Function>
			window.$$_jsonp[globalMethod_Token] = (this.callBack || this.CallBack || this.onSuccuess
).after(gc.bind(this), true);

			if(!this.params)this.params = {};
			
			// if(this.url.indexOf('?') != -1){
			// 	throw 'url不能包含查询号！如有 url 参数，应该将其写到 params 中。';
			// }
			
			// 创建 script tag，发起请求。
			// console.log('现进行跨域获取数据，url 为' + window.encodeURI(url));
			var script = document.createElement('script');
			this.params[this.callBackField] = globalMethod; // 加载 callBackField 字段到请求参数
			script.src	= appendParams2url(this.url, this.params);
			document.getElementsByTagName('head')[0].appendChild(script);


			// this.timeoutId = window.setTimeout(function(){
			// 	// gc?
		 //        if(this.isDone == true){
		 //        }else{
		 //        	var msg = '[jsonp]超时！ URL ' + this.url;
		 //        	this.onError && this.onError(this, msg);
			// 		throw msg;
		 //        }
		 //    }.bind(this), this.timeout);

			this.script = script;
		    this.globalMethod_Token = globalMethod_Token; // for Delete method at Window
		}
	};

	/**
	 * 移除 tag 标签及其事件hanlder
	 * 标记实例 ok token
	 * 如定时器有效，则取消之。
	 */
	function gc(){
		this.isDone = true;
		try{
			document.getElementsByTagName('head')[0].removeChild(this.script);
			this.timeoutId && window.clearTimeout(this.timeoutId);
			delete window.$$_jsonp[this.globalMethod_Token]; // 删除全局方法之引用

			console.log('[JSONP]gc OK!');
			return true;
		}catch(e){
			alert('[JSONP]gc fails!');
		}
	}

	function getRandom(){
		return parseInt(Math.random()*(200000-10000 + 1) + 10000); 
	}
	exports.jsonp = jsonp;

// var xhr2 = new XMLHttpRequest();
// xhr2.onreadystatechange = function(a){
// 	console.log(xhr2.status);
// 	// debugger;
// }
// xhr2.open('GET', 'http://u.3gtv.net/yuetv/download/index.jsp');
// xhr2.send();
	// var _xhr = Object.create(xhr);
	// _xhr.url = 'http://u.3gtv.net/yuetv/download/index.jsp';
	// _xhr.params = {j:9};
	// _xhr.onSuccuess =  function(){
	// 	debugger;
	// };
	// 	// debugger;
	// _xhr.request();

});